/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.     
 */ 
package shooter;

import com.jme3.app.*;
import com.jme3.app.state.*;
import com.jme3.asset.*;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.*;
import com.jme3.bullet.control.*;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.collision.*;
import com.jme3.effect.*;
import com.jme3.effect.shapes.EmitterSphereShape;
import com.jme3.input.*;
import com.jme3.input.controls.*;
import com.jme3.light.*;
import com.jme3.material.*;
import com.jme3.math.*;
import com.jme3.renderer.*;
import com.jme3.scene.*;
import com.jme3.scene.control.CameraControl.ControlDirection;
import com.jme3.scene.shape.*;
import com.jme3.texture.Texture;
import snails.*;

/**
 * @author ulfgur
 */
public class WorldAppState extends AbstractAppState
{
    //everything that has to do with the application as a whole
    AssetManager assetManager;
    Node rootNode;
    ViewPort viewPort;
    BulletAppState physics;
    SimpleApplication app;
    InputManager inputManager;
    ChaseCamera chaseCam;
    CameraNode camNode;
    FlyByCamera flycam;
    Camera cam;
    
    //everything to do with the world
        //This is a node that contains EVERY OBJECT
        //introduced by worldAppState for simple detaching.
        Node worldNode;
    
    
    //everything that has to do with the player
    Node playerNode;
    Snail player;

    public Snail getPlayer() {
        return player;
    }

    public void setPlayer(Snail player) {
        this.player = player;
    }
    
    
    
    //effects
    public static ParticleEmitter cannon;
    
    
    /**
     * create and initialise world.
     * @param app 
     */
    public WorldAppState(SimpleApplication app)
    {
        setUp(app);
        worldNode = new Node("WORLD");
        
        //sets up the flat, plain floor the game will be played on until we run out of important thins to fix.
        Box levelMesh = new Box(300, 9, 300);
            Geometry level = new Geometry("level", levelMesh);
            Material levelMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            Texture cube1Tex = assetManager.loadTexture("Textures/tex.png");
            levelMat.setTexture("ColorMap", cube1Tex);
            level.setMaterial(levelMat);
            worldNode.attachChild(level);
            
        app.getCamera().setLocation(new Vector3f(0, 20, 0));
        
        physics = new BulletAppState();
        app.getStateManager().attach(physics);
        
        
        Plane plane = new Plane();
        plane.setOriginNormal(new Vector3f(0, 10, 0), Vector3f.UNIT_Y);
        level.addControl(new RigidBodyControl(new PlaneCollisionShape(plane), 0));
        physics.getPhysicsSpace().add(level);
        
        setUpLights();
        initParticles();
        setUpPlayer();
        initKeys();
        
        
        
        
        
        Snail enemy = new Snail(5, 5, 150, "Enemy");
        
        //set up visual display of player
        Node enemyNode = new Node("enemy");
            Sphere enemyMesh = new Sphere(20, 20, 5);
            Geometry enemyGeom = new Geometry("enemy", enemyMesh);
            Material enemyMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            enemyMat.setColor("Color", ColorRGBA.Red);
            enemyGeom.setMaterial(enemyMat);
            enemyNode.attachChild(enemyGeom);
            worldNode.attachChild(enemyNode);
            
        enemyNode.addControl(enemy);
        enemy.init();
        enemy.warp(new Vector3f(40, 15, 40));
        physics.getPhysicsSpace().add(enemy);
        
    }
    
    /**
     * sets things up so that we can acess
     * all app assets directly.
     * @param app 
     */
    private void setUp(SimpleApplication app)
    {
        this.app = app;
        assetManager = app.getAssetManager();
        rootNode = app.getRootNode();
        viewPort = app.getViewPort();
        cam = app.getCamera();
        inputManager = app.getInputManager();
        flycam = app.getFlyByCamera();
    }
    

    /**
     * sets up lights
     */
    public void setUpLights()
    {
        // You must add a light to make the model visible
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f));
        rootNode.addLight(sun);
    }
    
    
    
    /**
     * sets up player
     *todo: change the chase cam to a camera node? 
     */
    public void setUpPlayer()
    {
        player = new Snail(5, 5, 150, "Player");
        
        //set up visual display of player
        playerNode = new Node("player");
            Sphere playerMesh = new Sphere(20, 20, 5);
            Geometry playerGeom = new Geometry("player", playerMesh);
            Material playerMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            playerMat.setColor("Color", ColorRGBA.Blue);
            playerGeom.setMaterial(playerMat);
            playerNode.attachChild(playerGeom);
            worldNode.attachChild(playerNode);
            
        playerNode.addControl(player);
        player.init();
        player.warp(new Vector3f(0, 15, 0));
        physics.getPhysicsSpace().add(player);
        
        
        /*
        chaseCam = new ChaseCamera(cam, playerNode, inputManager);
        chaseCam.setDragToRotate(false);
        chaseCam.setMaxDistance(100);
        chaseCam.setInvertVerticalAxis(true);
        chaseCam.setInvertHorizontalAxis(false);
        chaseCam.setSmoothMotion(true);
        
        */
        
        // Disable the default flyby cam
        flycam.setEnabled(false);
        //create the camera Node
        camNode = new CameraNode("Camera Node", cam);
        //This mode means that camera copies the movements of the target:
        camNode.setControlDir(ControlDirection.SpatialToCamera);
        //Attach the camNode to the target:
        playerNode.attachChild(camNode);
        //Move camNode, e.g. behind and above the target:
        camNode.setLocalTranslation(new Vector3f(0, 40, -60));
        //Rotate the camNode to look at the target:
        camNode.lookAt(playerNode.getLocalTranslation(), Vector3f.UNIT_Y);
        
        
       /*
        // Disable the default flyby cam
        app.getFlyByCamera().setEnabled(false);
        // Enable a chase cam for this target (typically the player).
        chaseCam = new ChaseCamera(cam, playerNode, inputManager);
        chaseCam.setDragToRotate(false);
        chaseCam.setMaxDistance(100);
        chaseCam.setInvertVerticalAxis(true);
        chaseCam.setInvertHorizontalAxis(false);
        chaseCam.setSmoothMotion(true);*/
    }
    
    
    /**
     * creates particles.
     * TODO: move things over to snail stuff.
     */
    public void initParticles()
    {
        cannon = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 200);
        Material mat_red = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
        mat_red.setTexture("Texture", assetManager.loadTexture("Effects/Untitled.png"));
        cannon.setMaterial(mat_red);
        cannon.scale(1, 1, 1);
        cannon.setImagesX(1); 
        cannon.setImagesY(1); // 1x2 texture animation
        cannon.setStartColor(ColorRGBA.Green);   // red
        cannon.setEndColor(ColorRGBA.White); // yellow
       // cannon.setShape(new EmitterSphereShape( Vector3f.ZERO, 2f));
        cannon.getParticleInfluencer().setInitialVelocity(new Vector3f(140, 0, 0));
        cannon.setStartSize(2);
        cannon.setEndSize(2);
        cannon.setLowLife(3f);
        cannon.setHighLife(3f);
        cannon.setFacingVelocity(true);
        cannon.emitAllParticles();
        cannon.setParticlesPerSec(90);
        cannon.setVelocityVariation(0);
        cannon.setLocalTranslation(0, 15, 0);
    }
    
    /**
     * initialises keys.
     * Note that this just
     * sets them up, doesn't 
     * actually bind them.
     */
    private void initKeys()
    {
          //TODO: figure out how to map oh, say, the RMB to cam movements.
            // You can map one or several inputs to one named action
            inputManager.addMapping("Foreward",  new KeyTrigger(KeyInput.KEY_W));
            inputManager.addMapping("Left",  new KeyTrigger(KeyInput.KEY_A));
            inputManager.addMapping("Right",  new KeyTrigger(KeyInput.KEY_D));
            inputManager.addMapping("LeftFire",  new KeyTrigger(KeyInput.KEY_Q));
            inputManager.addMapping("RightFire",  new KeyTrigger(KeyInput.KEY_E));
            
            //inputManager.addMapping("SwapEnemy",   new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
            enableMoveKeys();
            enableAttackKeys();
      }
    
    /**
     * enables movement keys
     */
    public void enableMoveKeys()
    {
        inputManager.addListener(movementListener,"Foreward");
        inputManager.addListener(movementListener,"Left");
        inputManager.addListener(movementListener,"Right");
    }
    
    /**
     * enables attack keys
     */
    public void enableAttackKeys()
    {
        inputManager.addListener(attackListener,"LeftFire");
        inputManager.addListener(attackListener,"RightFire");
    }
      
     
      
      /**
       * responds to input
       */
      private ActionListener movementListener = new ActionListener()
      {
            public void onAction(String name, boolean keyPressed, float tpf)
            {
                if (name.equals("Foreward") && keyPressed)
                {
                    player.startMoving();
                }
                if(name.equals("Foreward") && !keyPressed)
                {
                    player.stopMoving();
                }
                if (name.equals("Left") && keyPressed)
                {
                    player.startTurningLeft();
                }
                if(name.equals("Left") && !keyPressed)
                {
                   player.stopTurningLeft();
                }
                if (name.equals("Right") && keyPressed)
                {
                    player.startTurningRight();
                }
                if(name.equals("Right") && !keyPressed)
                {
                    player.stopTurningRight();
                }
            }
      };
    
      /**
       * responds to input
       */
      private ActionListener attackListener = new ActionListener()
      {
            public void onAction(String name, boolean keyPressed, float tpf)
            {
                if (name.equals("LeftFire") && keyPressed)
                {
                    player.fireLeft();
                }
                if(name.equals("RightFire") && !keyPressed)
                {
                    player.fireRight();
                }
            }
      };
    
      
      /**
       * casts rays for object detections.
       * @param to
       * @param loc
       * @param dir
       * @return 
       */
    public CollisionResults rayCast(Node to, Vector3f loc, Vector3f dir)
    {
        CollisionResults picks = new CollisionResults();
        Ray ray = new Ray(loc, dir);
        
        /*
        Arrow a = new Arrow(dir);
        Material arrowMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        arrowMat.setColor("Color", ColorRGBA.Red);
        Geometry geom = new Geometry("", a);
        geom.setMaterial(arrowMat);
        to.attachChild(geom);
        geom.setLocalTranslation(loc);
        */
        
        to.collideWith(ray, picks);
        return picks;
    }
    
    /**
     * picks a snail for cannon shots.
     * @param s
     * @param loc
     * @param dir
     * @return 
     */
    public Snail pickSnail(Snail s, Vector3f loc, Vector3f dir)
    {
        CollisionResults picks = rayCast(worldNode, loc, dir);
        Snail chosen = null;
        for(int a = 0; a < picks.size(); a++)
        {
            Node current = picks.getCollision(a).getGeometry().getParent();
            if(current.getControl(Snail.class) != null)
            {
                Snail temp = current.getControl(Snail.class);
                if(!s.equals(temp))
                {
                    chosen = temp;
                    break;
                }
            }
        }
        return chosen;
    }
    
    
    
    
    @Override
    public void setEnabled(boolean enabled)
    {
      super.setEnabled(enabled);
      if(enabled)
      {
          if(worldNode != null)
          {
              rootNode.attachChild(worldNode);
              physics.setDebugEnabled(true);
          }
      }else
      {
          if(worldNode != null)
          {
              rootNode.detachChild(worldNode);
              physics.setDebugEnabled(false);
          }
      }
      physics.setEnabled(enabled);
    }
    
    
    
    @Override
    public void update(float tpf)
    {
        
    }
    
}